package com.kede.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.kede.annotation.Log;
import com.kede.config.FileProperties;
import com.kede.dictenum.BoolDictEnum;
import com.kede.domain.LocalStorage;
import com.kede.domain.QiniuConfig;
import com.kede.domain.QiniuContent;
import com.kede.domain.ResourceFile;
import com.kede.exception.BadRequestException;
import com.kede.repository.QiNiuConfigRepository;
import com.kede.repository.QiniuContentRepository;
import com.kede.repository.ResourceAreaMapRepository;
import com.kede.rest.po.ResourceAreaMapPo;
import com.kede.service.QiNiuService;
import com.kede.service.ResourceAreaMapService;
import com.kede.utils.*;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import lombok.RequiredArgsConstructor;
import com.kede.repository.ResourceFileRepository;
import com.kede.service.ResourceFileService;
import com.kede.service.dto.ResourceFileDto;
import com.kede.service.dto.ResourceFileQueryCriteria;
import com.kede.service.mapstruct.ResourceFileMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.net.URLEncoder;
import java.util.*;
import java.io.IOException;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;

/**
* @description 服务实现
* @author Pr.li
* @date 2023-04-20
**/
@Service
@RequiredArgsConstructor
@Slf4j
public class ResourceFileServiceImpl implements ResourceFileService {

    private final ResourceFileRepository resourceFileRepository;
    private final ResourceFileMapper resourceFileMapper;
    private final FileProperties properties;
    private final QiNiuService qiNiuService;
    private final QiNiuConfigRepository qiNiuConfigRepository;
    private final QiniuContentRepository qiniuContentRepository;

    private final ResourceAreaMapRepository resourceAreaMapRepository;

    private final ResourceAreaMapService resourceAreaMapService;

    @Value("${qiniu.max-size}")
    private Long maxSize;

    @Override
    public Map<String,Object> queryAll(ResourceFileQueryCriteria criteria, ResourceAreaMapPo resourceAreaMapPo, Pageable pageable){

        List<Integer> resourceIds = resourceAreaMapService.getResourceIdsByAreaIds(resourceAreaMapPo.getAreaValues());
        if (CollUtil.isNotEmpty((resourceIds))){
            criteria.setId(resourceIds);
        }else if(CollUtil.isNotEmpty((resourceAreaMapPo.getAreaValues()))){
            resourceIds.add(0);
            criteria.setId(resourceIds);
        }
//        设置回资源id列表

        Page<ResourceFile> page = resourceFileRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        return PageUtil.toPage(page.map(resourceFileMapper::toDto));
    }

    @Override
    public List<ResourceFileDto> queryAll(ResourceFileQueryCriteria criteria){
        return resourceFileMapper.toDto(resourceFileRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
    }

    @Override
    @Transactional
    public ResourceFileDto findById(Integer id) {
        ResourceFile resourceFile = resourceFileRepository.findById(id).orElseGet(ResourceFile::new);
        ValidationUtil.isNull(resourceFile.getId(),"ResourceFile","id",id);
        return resourceFileMapper.toDto(resourceFile);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResourceFileDto create(ResourceFile resources,MultipartFile file) {

        if (resources.getIslocal().equals(BoolDictEnum.TRUE.getCode())){
            resources = saveLocalFile(resources,file);
        }else {
            resources = saveQiniuFile(resources,file);
        }
        return resourceFileMapper.toDto(resourceFileRepository.save(resources));
    }

    public ResourceFile saveQiniuFile(ResourceFile resourceFile,MultipartFile file){
        FileUtil.checkSize(maxSize, file.getSize());
        QiniuConfig qiniuConfig = qiNiuService.find();
        if(qiniuConfig ==null || qiniuConfig.getId() == null){
            throw new BadRequestException("请先添加相应配置，再操作");
        }
        // 构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(QiNiuUtil.getRegion(qiniuConfig.getZone()));
        UploadManager uploadManager = new UploadManager(cfg);
        Auth auth = Auth.create(qiniuConfig.getAccessKey(), qiniuConfig.getSecretKey());
        String upToken = auth.uploadToken(qiniuConfig.getBucket());
        try {
            String encodedFileName = URLEncoder.encode(file.getOriginalFilename(), "utf-8").replace("+", "%20");
            String key = encodedFileName;
            if(resourceFileRepository.findByName(key) != null) {
                key = QiNiuUtil.getKey(key);
            }
            Response response = uploadManager.put(file.getBytes(), key, upToken);
            //解析上传成功的结果

            DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
            resourceFile.setSuffix(FileUtil.getExtensionName(putRet.key));
            resourceFile.setBucket(qiniuConfig.getBucket());
            resourceFile.setType(qiniuConfig.getType());
//            resourceFile.setName(FileUtil.getFileNameNoEx(putRet.key));
            resourceFile.setRealName(putRet.key);
            resourceFile.setSize(FileUtil.getSize(Integer.parseInt(file.getSize()+"")));
            return resourceFile;
        } catch (Exception e) {
            throw new BadRequestException(e.getMessage());
        }
    }

    public static String uploadPrefix = "uploads";

    public ResourceFile saveLocalFile(ResourceFile resources, MultipartFile multipartFile){
        String fileSize = FileUtil.getSize(multipartFile.getSize());
        FileUtil.checkSize(properties.getMaxSize(), multipartFile.getSize());
        String suffix = FileUtil.getExtensionName(multipartFile.getOriginalFilename());
        String type =   FileUtil.getFileType(suffix);
        String path = uploadPrefix + File.separatorChar+ type;
        StringBuilder filepath = new StringBuilder();
        filepath.append(properties.getPath().getPath());
        filepath.append(File.separatorChar);
        filepath.append(path);
        filepath.append(File.separatorChar);
        File file = FileUtil.upload(multipartFile, filepath.toString());
        if(ObjectUtil.isNull(file)){
            throw new BadRequestException("上传失败");
        }
        try {
            resources.setPath(path);
            resources.setSuffix(suffix);
            resources.setType(type);
            resources.setRealName(file.getName());
            resources.setSize(fileSize);
            return resources;
        }catch (Exception e){
            FileUtil.del(file);
            throw e;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ResourceFile resources) {
        ResourceFile resourceFile = resourceFileRepository.findById(resources.getId()).orElseGet(ResourceFile::new);
        ValidationUtil.isNull( resourceFile.getId(),"ResourceFile","id",resources.getId());
        resourceFile.copy(resources);
        resourceFileRepository.save(resourceFile);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            ResourceFile storage = resourceFileRepository.findById(id).orElseGet(ResourceFile::new);
            if (storage.getIslocal().equals(BoolDictEnum.TRUE.getCode())){
                String filepath = properties.getPath().getPath() + File.separatorChar + storage.getPath() + File.separatorChar +storage.getRealName();
                log.debug("==================================");
                boolean del = FileUtil.del(filepath);
                log.debug("==================================");

            }else {
                QiniuConfig config = qiNiuService.find();
                //构造一个带指定Zone对象的配置类
                Configuration cfg = new Configuration(QiNiuUtil.getRegion(config.getZone()));
                Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey());
                BucketManager bucketManager = new BucketManager(auth, cfg);
                try {
                    bucketManager.delete(storage.getBucket(), storage.getRealName() );
                } catch (QiniuException ex) {
                }
            }
            resourceFileRepository.deleteById(id);
        }
        List<Integer> resourceIds = Arrays.stream(ids).distinct().collect(Collectors.toList());
        resourceAreaMapRepository.deleteByResourceIdIn(resourceIds);
    }

    @Override
    public void download(List<ResourceFileDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (ResourceFileDto resourceFile : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("文件真实的名称", resourceFile.getRealName());
            map.put("七牛云bucket标识", resourceFile.getBucket());
            map.put("文件名", resourceFile.getName());
            map.put("后缀", resourceFile.getSuffix());
            map.put("存储路径", resourceFile.getPath());
            map.put("类型", resourceFile.getType());
            map.put("大小", resourceFile.getSize());
            map.put("文件关联路径  有http协议为外链，否则是内部页面", resourceFile.getLink());
            map.put("创建者", resourceFile.getCreateBy());
            map.put("更新者", resourceFile.getUpdateBy());
            map.put("创建日期", resourceFile.getCreateTime());
            map.put("更新时间", resourceFile.getUpdateTime());
            map.put("资源对应 功能类目（与系统枚举ResourceFun对应）", resourceFile.getFunction());
            map.put("是否本地本地存储", resourceFile.getIslocal());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}